Bump deprecated GitHub Action versions in CI - #15
Conversation
CI emits "deprecated Node.js version" warnings because workflows pin node16-era actions. Bump every third-party action to its current major (checked release notes of each crossed major for breaking changes): checkout v2/v3/v4->v7, cache v3->v6, setup-miniconda v2->v4, autopep8 v1->v2, create-pull-request v3/v7->v8, upload-artifact v4->v7, setup-julia v2->v3, julia cache v2->v3. Also rename the deprecated setup-miniconda input auto-activate-base to auto-activate (same conda setting; v4 maps both identically but warns on the old name). conditional-build-matrix@main (matrix logic, already node20) and ovsds/create-or-update-unique-issue-action@v1 (latest major, composite) are left untouched. Verified: all five files YAML-parse, every input we pass exists in the new majors' action.yml, all major tags resolve via git ls-remote; Actions cannot run locally so PR CI is the real test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bumping majors left three problems that the version numbers alone don't
fix, so handle them here.
JoshuaTheMiller/conditional-build-matrix is still node20, is referenced
through a mutable `main` branch, and its `filter` input interpolated the
whole value of secrets.LMFDB_CI_ACCESS into third-party action code when
all it needed was whether the secret is set. Replace it with a local
`python3` step that reads matrix_includes.json and keeps only devmirror
entries when HAS_LMFDB_CI_ACCESS is not "true", emitting the same
{"include":[...]} shape the action produced under its default
addInclude: true. The job output and the fromJson consumer are unchanged.
ovsds/create-or-update-unique-issue-action wraps
actions-cool/issues-helper@v3, all of whose tags were repointed at
imposter commits that exfiltrate CI/CD credentials (2026-05-18).
GitHub has since blocked that repository, which is why every scheduled
snippet_test run since 2026-06-01 has died at "Prepare all required
actions" with "Repository access blocked" -- no step in those runs ever
executed. Replace it with a gh-CLI step in a separate report-snippet-errors
job that receives the report as an artifact, so the checkout/Conda/Julia/
Oscar job never holds an issues:write token, and the issue body comes from
--body-file rather than a shell or workflow-expression interpolation.
julia-actions/cache@v3 defaults save-always and delete-old-caches to true;
v2 saved only on success and needed no actions:write. Pin both to false to
keep the old semantics and the read-only token.
Also declare `permissions: contents: read` at the top of both workflows.
Verified locally: all five workflow/action files YAML-parse; the new
matrix step's `run:` body executed under bash yields 22 entries (both
servers) with HAS_LMFDB_CI_ACCESS=true and 12 devmirror-only entries with
false, as compact single-line JSON; `bash -n` on the gh reporting script;
the detect step reports exists=true/false correctly; the jq title match
returns the number only on an exact title. Verified against the upstream
action.yml files that julia-actions/cache@v3 has both inputs and that
download-artifact@v8 (node24) takes name/path.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Pushed 1.
|
| when | what |
|---|---|
| 2026-05-15T02:09:47Z | last run in which actions-cool/issues-helper@v3 actually executed, resolving to 200c78641dbf33838311e5a1e0c31bbdb92d7cf0 |
| 2026-05-18T19:11:08Z | reported compromise (all tags repointed at imposter commits) |
| 2026-05-19T00:33:29Z | GitHub blocked the repository ("reason": "tos", still 403 today) |
| 2026-06-01 onward | every scheduled run dies before any step executes |
All 6 snippet_test.yml runs at or after the compromise timestamp, 5 in LMFDB/lmfdb (06-01, 06-15, 07-01, 07-15, 08-01) and 1 in roed-math/lmfdb (08-01), failed at step 1, "Set up job", inside Prepare all required actions, with ##[error]Repository access blocked. Not one workflow step ran in any of them: no checkout, no setup, no issue action. Grepping all six logs for actions-cool/issues-helper|setup-bun|t.m-kosche.com|ACTIONS_ID_TOKEN_REQUEST returns no matches, and the same grep over the 2704-line log of the last pre-compromise run (2026-05-15) is also clean.
Because the next scheduled run after the compromise was 2026-06-01, by which time GitHub had already blocked the repo, there is no run to treat as compromised and no credential to rotate.
Worth noting as a side effect: this is also why the scheduled snippet workflow has been completely dead since 2026-06-01. The earlier failures (2026-03-01 through 2026-05-15) were the Check for diff step doing its job at the end of an otherwise complete run; the June-onward ones never got as far as checking out the repo. Change 2 puts the workflow back into service.
Validation
Actions still cannot be run locally, so the checks on this PR remain the real verification, but the pieces that are plain code were exercised directly:
- All 5 workflow/composite-action files
yaml.safe_loadcleanly. - The new matrix step's
run:body was executed through bash exactly as written (heredoc and all) against a temporaryGITHUB_OUTPUT:HAS_LMFDB_CI_ACCESS=trueyields 22 entries covering both servers, matchingmatrix_includes.jsonentry for entry;falseyields 12 entries, every one of themserver == "devmirror", and equal to the devmirror subset of the file. Both write exactly one output line, keymatrix, holding compact single-line JSON whose only top-level key isinclude. bash -non theghreporting script; thejqtitle filter returns a number only on an exact title match (empty list gives empty, near-miss title gives empty, exact title gives the number).- The detect step reports
exists=falsewith nosnippet_error_file.mdpresent andexists=truewith one. - Structural assertions on the parsed YAML: only
report-snippet-errorscarriesissues: write, the upload step precedes the delete step,Read error fileis gone, and the three diff-check steps survive in order. - Upstream
action.ymlre-read for every input this PR relies on:julia-actions/cache@v3(save-always,delete-old-caches),actions/download-artifact@v8(node24;name,path),actions/upload-artifact@v7(if-no-files-found,retention-days),peter-evans/create-pull-request@v8andpeter-evans/autopep8@v2. Every remaininguses:under.github/is now either a first-party GitHub action on node24,julia-actions/*@v3,conda-incubator/setup-miniconda@v4,peter-evans/*, or the local composite action.
Not exercised: the actual issue create/update path, which needs a deliberate snippet_error_file.md on a throwaway branch, and the autopep8.yml / snippet_generate.yml write paths, which only run on push to main.
The Tests workflow is queued at 81e9cb0f5 but has not started yet: this fork currently has ~95 runs waiting behind a separate CI campaign, so Initial setup (the job that exercises the new matrix step) has not had a runner assigned. This should not be merged until that job goes green and the test matrix fans out as expected.
Deliberately left for follow-up
Keeping this PR to the bump plus the three blockers: SHA-pinning the remaining third-party actions, Dependabot for the github-actions ecosystem, and explicit permissions: in autopep8.yml and snippet_generate.yml (both need contents: write plus pull-requests: write for create-pull-request, so those want scoping rather than clamping, which is a bigger edit than this PR should carry).
One unrelated pre-existing bug spotted while reviewing those two files, not touched here: autopep8.yml guards the formatter step with if: ${{ github.repository }} == 'LMFDB/lmfdb', which expands to the constant string LMFDB/lmfdb == 'LMFDB/lmfdb' and is therefore always truthy. The fork guard does not guard. It wants if: github.repository == 'LMFDB/lmfdb', and the Create Pull Request step below it has no guard at all.
|
GPT signed off. |
|
Superseded by LMFDB#7152, opened upstream from this same branch. Closing here; review continues upstream. (The write-up on the two removed actions stays linked from the new PR body, so this thread remains the reference for it.) |
CI runs currently emit "The following actions use a deprecated Node.js version" warnings because our workflows pin node16-era actions. This bumps every third-party action in
.github/workflows/and thesnippet_setupcomposite action to its current major: checkout v2/v3/v4→v7, cache v3→v6, setup-miniconda v2→v4, peter-evans/autopep8 v1→v2, create-pull-request v3/v7→v8, upload-artifact v4→v7, and the two julia-actions v2→v3. Release notes of each crossed major were checked: none of the breaking changes touch the inputs or triggers we use, and setup-miniconda v4 still defaults to the runner-bundled Miniconda, so the conda cache key/path is unchanged. The deprecated setup-miniconda inputauto-activate-baseis renamed toauto-activate(same conda setting, silences its deprecation warning).Two of the third-party actions could not be fixed by a version number, so they are removed instead (see this comment for the full write-up):
JoshuaTheMiller/conditional-build-matrix@mainwas still node20, was pinned to a mutable branch, and interpolated the entire value ofsecrets.LMFDB_CI_ACCESSinto third-party action code when all it needed was whether the secret exists. It is replaced by a localpython3step that readsmatrix_includes.jsonand keeps onlydevmirrorentries when the secret is absent, emitting the same{"include":[...]}value. The job output and thefromJson(...)consumer are unchanged.ovsds/create-or-update-unique-issue-action@v1wrappedactions-cool/issues-helper@v3, all of whose tags were repointed at credential-exfiltrating imposter commits on 2026-05-18; GitHub blocked that repository on 2026-05-19, which is why every scheduledsnippet_testrun since 2026-06-01 has died atPrepare all required actionswithout executing a single step. It is replaced by agh-CLI step in a separatereport-snippet-errorsjob that receives the report as an artifact, so the checkout/Conda/Julia/Oscar job never holds anissues: writetoken and the issue body is read from a file rather than interpolated.julia-actions/cache@v3also getssave-always: falseanddelete-old-caches: false, restoring v2's success-only save behavior and keeping the snippet job read-only. Both workflows now declarepermissions: contents: read.An audit of every
snippet_testrun at or after the compromise timestamp found no execution of the compromised action and no exfiltration indicators, so no credential rotation is needed; details are in the comment linked above.Actions can't be exercised locally, so the checks on this PR are the real verification: the Tests workflow covers the new matrix step plus checkout/miniconda/cache directly, while the autopep8 and snippet workflows only run on push to main or on schedule. The matrix step's shell body, the reporting script's syntax, and the
jqtitle filter were all run locally against fixtures.Addresses LMFDB#6220.
🤖 Generated with Claude Code